<template>
  <div style="width: 280px" v-loading="isLoading">
    <el-col type="flex" justify="center" align="center">
      X:<el-input v-model="axisX" style="width: 60px" readonly></el-input>
      Y:<el-input v-model="axisY" style="width: 60px" readonly></el-input>
      Z:<el-input v-model="axisZ" style="width: 60px" readonly></el-input>
    </el-col>
    <div>
      <div id="container" style="background-color: black;z-index: 1;width: 420px;height: 420px;margin-left: 280px;position: absolute"></div>
      <canvas id="fslpanel"  width="280" height="420" style="background-color: black"
              @mousedown="handleMouseDown($event)"
              @mousemove="handleAxisChange($event)"
              @mouseup="mouseFlag = true"></canvas>
    </div>
    <el-row v-if="importable" type="flex">
      <el-button @click="importBase">Base</el-button>
      <el-button @click="importSkeleton">Skeleton</el-button>
      <el-button @click="importtbssTfceCorrp">TBSS Tfce Corrp</el-button>
      <el-button @click="VolRendering">Volume Rendering</el-button>
    </el-row>
  </div>
</template>

<script>
  /* eslint-disable */
  import * as THREE from 'three'
  import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
  import * as NIFTI from 'nifti-reader-js'
  import { build } from './build-model.js'

  const y2rColor = (function (width,height) {
    let canvas = document.createElement("canvas");
    canvas.width = width;
    canvas.height = height;
    let ctx = canvas.getContext("2d");
    let grd = ctx.createLinearGradient(0, 0, width, height);
    grd.addColorStop(0.1, "#fffd44");
    grd.addColorStop(1.0, "#f2130a");
    // 绘制渐变色条
    ctx.fillStyle = grd;
    ctx.fillRect(0, 0, width, height);
    return ctx.getImageData(0,0,width, height).data;
  })(100,1)
  const y2gColor = (function (width,height) {
    let canvas = document.createElement("canvas");
    canvas.width = width;
    canvas.height = height;
    let ctx = canvas.getContext("2d");
    let grd = ctx.createLinearGradient(0, 0, width, height);
    grd.addColorStop(0.1, "#fffd44");
    grd.addColorStop(1.0, "#25fa2b");
    // 绘制渐变色条
    ctx.fillStyle = grd;
    ctx.fillRect(0, 0, width, height);
    return ctx.getImageData(0,0,width, height).data;
  })(100,1)
  export default {
    data(){
      return {
        isLoading: false,
        baseHeader: null,
        baseImage: null,
        skeletonImage: null,
        tbssImage: null,
        mouseFlag: true,
        axisX: 90,
        axisY: 100,
        axisZ: 90,
        importable: true,
        gl: {
          renderer: null,
          controls: null,
          camera: null,
          scene1: new THREE.Scene(),
          scene2: new THREE.Scene(),
          colorCubeMat: null,
          volMat: null,
          rtTexture: null,
          box: null,
          target: null
        },
      }
    },
    methods: {
      init(){
        let container = document.getElementById( 'container' );
        this.gl.renderer = new THREE.WebGLRenderer();
        this.gl.renderer.setSize( container.clientWidth, container.clientHeight );
        container.appendChild( this.gl.renderer.domElement );

        this.gl.camera = new THREE.PerspectiveCamera( 40, container.clientWidth/container.clientHeight, 0.01, 3000.0 );
        this.gl.camera.position.z = 2.0;

        this.gl.controls = new OrbitControls( this.gl.camera, container );
        this.gl.controls.target.set( 0.0, 0.0, 0.0 );
      },
      render() {
        let { rtTexture, camera, renderer, scene1, scene2 } = this.gl
        renderer.setRenderTarget(rtTexture)
        renderer.render( scene1, camera );
        renderer.setRenderTarget(null)
        renderer.render( scene2, camera );
      },
      // 被下边两个load方法调用
      load(buffer) {
        let imageBuffer
        let niftiHeader
        let niftiImage
        if (NIFTI.isCompressed(buffer)) {
          buffer = NIFTI.decompress(buffer)
          console.log('isCompressed')
        }
        if (NIFTI.isNIFTI(buffer)) {
          niftiHeader = NIFTI.readHeader(buffer);
          imageBuffer = NIFTI.readImage(niftiHeader, buffer);
        } else throw 'fail';
        switch (niftiHeader.datatypeCode){
          case 1: throw 'don\'t support this data type, 太麻烦不想写';
          case 2: niftiImage = new Uint8Array(imageBuffer, 0, imageBuffer.byteLength);break;
          case 4: niftiImage = new Int16Array(imageBuffer);break;
          case 8: niftiImage = new Int32Array(imageBuffer);break;
          case 16: niftiImage = new Float32Array(imageBuffer);break;
          case 32: throw 'don\'t support this data type, 不知道complex是啥格式';
          case 64: niftiImage = new Float64Array(imageBuffer);break;
          case 128: throw 'don\'t support this data type, 太麻烦不想写';
          default:
            throw 'don\'t support this data type, 先空着';
        }
        return {niftiImage:niftiImage,niftiHeader:niftiHeader}
      },
      // 为DTI模块准备的，一次性绘制加载三个图像来绘制
      loadAllImage(baseImageUri, skeletonImageUri, tbssImageUri){
        this.isLoading = true
        this.baseImage = null;
        this.skeletonImage = null;
        this.tbssImage = null;
        const self = this
        fetch('http:' + window.g.API_URL + baseImageUri, {
          headers: {
            'Accept': 'application/octet-stream',
            'content-type': 'application/octet-stream'
          },
          method: 'GET',
          credentials: 'include'
        }).then(response => {
          if(response.ok) return response.arrayBuffer()
          else return Promise.reject('can not find '+baseImageUri)
        }).then(data => {
          if (data) {
            let niftiInfo = self.load(data);
            self.baseHeader = niftiInfo.niftiHeader;
            self.baseImage = niftiInfo.niftiImage;
            self.draw();
          }
          return fetch('http:' + window.g.API_URL + skeletonImageUri, {
            headers: {
              'Accept': 'application/octet-stream',
              'content-type': 'application/octet-stream'
            },
            method: 'GET',
            credentials: 'include'
          })
        }).then(response => {
          if(response.ok) return response.arrayBuffer()
          else return Promise.reject('can not find '+skeletonImageUri)
        }).then(data => {
          if (data) {
            let niftiInfo = self.load(data);
            self.skeletonImage = niftiInfo.niftiImage;
            self.draw();
          }
          return fetch('http:' + window.g.API_URL + tbssImageUri, {
            headers: {
              'Accept': 'application/octet-stream',
              'content-type': 'application/octet-stream'
            },
            method: 'GET',
            credentials: 'include'
          })
        }).then(response => {
          if(response.ok) return response.arrayBuffer()
          else return Promise.reject('can not find '+tbssImageUri)
        }).then(data => {
          if (data) {
            let niftiInfo = self.load(data);
            self.tbssImage = niftiInfo.niftiImage;
            self.draw();
          }
          self.isLoading = false
        }).catch(err=>{
          // self.$message({message: err, type: 'warning'})
          const h = self.$createElement
          self.$notify({
            title: 'Tips: can not get file',
            message: err.substring(err.lastIndexOf('/')+1),
            type: 'warning',
            duration: 0
          });
          self.isLoading = false
        })
      },
      // 为BrainNetViewer模块准备的，专门用于绘制aal.nii
      loadBaseOnly(buffer){
        this.importable = false
        let niftiInfo = this.load(buffer);
        this.baseHeader = niftiInfo.niftiHeader;
        this.baseImage = niftiInfo.niftiImage;
        this.draw()
      },
      // 根据datatypeCode选择相应的绘制方法drawWithGray或drawWithROI
      draw(){
        switch (this.baseHeader.datatypeCode){
          case 1: throw 'don\'t support this data type, 太麻烦不想写';
          case 2: this.drawWithGray();break;
          case 4: this.drawWithGray();break;
          case 8: this.drawWithGray();break;
          case 16: this.drawWithGray();break;
          case 32: throw 'don\'t support this data type, 不知道complex是啥格式';
          case 64: this.drawWithGray();break;
          case 128:throw 'don\'t support this data type, 没有准备相应的绘图方法';
          default:
            throw 'don\'t support this data type, 先空着';
        }
      },
      drawWithGray() {
        let x = this.axisX, y = this.axisY, z = this.axisZ
        let canvas = document.getElementById("fslpanel");
        let ctx = canvas.getContext("2d");
        let cols = this.baseHeader.dims[1];
        let rows = this.baseHeader.dims[2];
        let slices = this.baseHeader.dims[3];
        let offset, index, rgb, gray, skeleton, tbss;
        let imageData = ctx.createImageData(cols, rows);        // 创建画图区
        ctx.clearRect(0,0,canvas.width,canvas.height)
        let max = this.baseHeader.maxv;
        let min = this.baseHeader.minv;
        for (let ctrY = 0; ctrY < rows; ctrY++) {
          for (let ctrX = 0; ctrX < cols; ctrX++) {
            offset = ((((z * rows) + ctrY) * cols) + ctrX);
            index = (((rows - ctrY) * cols) + ctrX) * 4;
            gray = this.baseImage == null ? 0:this.baseImage[offset];
            skeleton = this.skeletonImage == null ? 0:this.skeletonImage[offset];
            tbss = this.tbssImage == null ? 0:this.tbssImage[offset];
            if (gray !== 0) {
              if(max>1||min<0) imageData.data[index] = imageData.data[index + 1] = imageData.data[index + 2] = Math.floor((gray-min)/(max-min)*255);
              else imageData.data[index] = imageData.data[index + 1] = imageData.data[index + 2] = parseInt(gray * 255);
            }
            if (skeleton > 0.2) {
              imageData.data[index] = 0;
              imageData.data[index + 1] = 255;
              imageData.data[index + 2] = 0;
            }
            if (tbss !== 0) {
              rgb = Math.floor(tbss*100)*4
              imageData.data[index] = y2rColor[rgb];
              imageData.data[index + 1] = y2rColor[rgb + 1];
              imageData.data[index + 2] = y2rColor[rgb + 2];
            }
            imageData.data[index + 3] = 255;//透明度
          }
        }
        for (let i = 0; i < rows; i++) {
          index = ((i * cols) + x) * 4;
          imageData.data[index] = imageData.data[index + 1] = 0
          imageData.data[index + 2] = imageData.data[index + 3] = 255
        }
        for (let i = 0; i < cols; i++) {
          index = (((rows - y) * cols) + i) * 4;
          imageData.data[index] = imageData.data[index + 1] = 0
          imageData.data[index + 2] = imageData.data[index + 3] = 255
        }
        let newCanvas = document.createElement("canvas");
        newCanvas.width = imageData.width
        newCanvas.height = imageData.height
        newCanvas.getContext("2d").putImageData(imageData, 0, 0)
        ctx.drawImage(newCanvas, 0, 0, newCanvas.width, newCanvas.height, 14, 0, 252, 280);

        imageData = ctx.createImageData(cols, slices);
        for (let ctrZ = 0; ctrZ < slices; ctrZ++) {
          for (let ctrX = 0; ctrX < cols; ctrX++) {
            offset = ((((ctrZ * rows) + y) * cols) + ctrX);
            index = (((slices - ctrZ - 1) * cols) + ctrX) * 4;
            gray = this.baseImage == null ? 0:this.baseImage[offset];
            skeleton = this.skeletonImage == null ? 0:this.skeletonImage[offset];
            tbss = this.tbssImage == null ? 0:this.tbssImage[offset];
            if (gray !== 0) {
              if(max>1||min<0) imageData.data[index] = imageData.data[index + 1] = imageData.data[index + 2] = Math.floor((gray-min)/(max-min)*255);
              else imageData.data[index] = imageData.data[index + 1] = imageData.data[index + 2] = parseInt(gray * 255);
            }
            if (skeleton > 0.2) {
              imageData.data[index] = 0;
              imageData.data[index + 1] = 255;
              imageData.data[index + 2] = 0;
            }
            if (tbss !== 0) {
              rgb = Math.floor(tbss*100)*4
              imageData.data[index] = y2rColor[rgb];
              imageData.data[index + 1] = y2rColor[rgb + 1];
              imageData.data[index + 2] = y2rColor[rgb + 2];
            }
            imageData.data[index + 3] = 255;//透明度
          }
        }
        for (let i = 0; i < slices; i++) {
          index = (((slices - i - 1) * cols) + x) * 4;
          imageData.data[index] = imageData.data[index + 1] = 0
          imageData.data[index + 2] = imageData.data[index + 3] = 255
        }
        for (let i = 0; i < cols; i++) {
          index = (((slices - z - 1) * cols) + i) * 4;
          imageData.data[index] = imageData.data[index + 1] = 0
          imageData.data[index + 2] = imageData.data[index + 3] = 255
        }
        newCanvas = document.createElement("canvas");
        newCanvas.width = imageData.width
        newCanvas.height = imageData.height
        newCanvas.getContext("2d").putImageData(imageData, 0, 0)
        ctx.drawImage(newCanvas, 0, 0, newCanvas.width, newCanvas.height, 7, 287, 126, 126);

        imageData = ctx.createImageData(rows, slices);
        for (let ctrZ = 0; ctrZ < slices; ctrZ++) {
          for (let ctrY = 0; ctrY < rows; ctrY++) {
            offset = ((((ctrZ * rows) + ctrY) * cols) + x);
            index = (((slices - ctrZ - 1) * rows) + (rows-ctrY)) * 4;
            gray = this.baseImage == null ? 0:this.baseImage[offset];
            skeleton = this.skeletonImage == null ? 0:this.skeletonImage[offset];
            tbss = this.tbssImage == null ? 0:this.tbssImage[offset];
            if (gray !== 0) {
              if(max>1||min<0) imageData.data[index] = imageData.data[index + 1] = imageData.data[index + 2] = Math.floor((gray-min)/(max-min)*255);
              else imageData.data[index] = imageData.data[index + 1] = imageData.data[index + 2] = parseInt(gray * 255);
            }
            if (skeleton > 0.2) {
              // rgb = y2rColor(skeleton).match(/\d+/g)
              imageData.data[index] = 0;
              imageData.data[index + 1] = 255;
              imageData.data[index + 2] = 0;
            }
            if (tbss !== 0) {
              rgb = Math.floor(tbss*100)*4
              imageData.data[index] = y2rColor[rgb];
              imageData.data[index + 1] = y2rColor[rgb + 1];
              imageData.data[index + 2] = y2rColor[rgb + 2];
            }
            imageData.data[index + 3] = 255;//透明度
          }
        }
        for (let i = 0; i < slices; i++) {
          index = ((i * rows) + (rows-y)) * 4;
          imageData.data[index] = imageData.data[index + 1] = 0
          imageData.data[index + 2] = imageData.data[index + 3] = 255
        }
        for (let i = 0; i < rows; i++) {
          index = (((slices - z - 1) * rows) + i) * 4;
          imageData.data[index] = imageData.data[index + 1] = 0
          imageData.data[index + 2] = imageData.data[index + 3] = 255
        }
        newCanvas = document.createElement("canvas");
        newCanvas.width = imageData.width
        newCanvas.height = imageData.height
        newCanvas.getContext("2d").putImageData(imageData, 0, 0)
        ctx.drawImage(newCanvas, 0, 0, newCanvas.width, newCanvas.height, 140, 287, 140, 126);
        // ctx.putImageData(imageData, 0, 0)
      },
      // 本地导入影像数据
      importBase(){
        let input = document.createElement('input');
        const self = this
        input.type = 'file';
        input.accept = '.nii,.nii.gz';
        input.onchange = function(ev) {
          const file = ev.target.files[0];
          let reader = new FileReader();
          reader.onload = e=> {
            let buffer = reader.result;
            let niftiInfo = self.load(buffer);
            let baseImage;
            self.baseHeader = niftiInfo.niftiHeader;
            baseImage = self.baseImage = niftiInfo.niftiImage;
            let len = baseImage.length,min = Number.MAX_VALUE,max = Number.MIN_VALUE;
            for(let i = 0;i < len;i++){
              min = Math.min(baseImage[i],min);
              max = Math.max(baseImage[i],max);
            }
            self.baseHeader['maxv'] = max;
            self.baseHeader['minv'] = min;
            self.draw()
          };
          reader.readAsArrayBuffer(file);
          return false;
        };
        input.click();
      },
      importSkeleton(){
        let input = document.createElement('input');
        const self = this
        input.type = 'file';
        input.accept = '.nii,.nii.gz';
        input.onchange = function(ev) {
          const file = ev.target.files[0];
          let reader = new FileReader();
          reader.onload = e=> {
            let buffer = reader.result;
            let niftiInfo = self.load(buffer);
            self.skeletonImage = niftiInfo.niftiImage;
            self.draw()
          };
          reader.readAsArrayBuffer(file);
          return false;
        };
        input.click();
      },
      importtbssTfceCorrp(){
        let input = document.createElement('input');
        const self = this
        input.type = 'file';
        input.accept = '.nii,.nii.gz';
        input.onchange = function(ev) {
          const file = ev.target.files[0];
          let reader = new FileReader();
          reader.onload = e=> {
            let buffer = reader.result;
            let niftiInfo = self.load(buffer);
            self.tbssImage = niftiInfo.niftiImage;
            self.draw()
          };
          reader.readAsArrayBuffer(file);
          return false;
        };
        input.click();
      },
      VolRendering(){
        const render = this.render.bind(this)
        this.init()
        let dims = this.baseHeader.dims;
        let canvas = document.createElement("canvas");
        let boxLen = Math.max(dims[1],dims[2],dims[3])
        canvas.width = boxLen;
        canvas.height = boxLen*boxLen;
        let ctx = canvas.getContext("2d");
        let imageData = ctx.createImageData(canvas.width, canvas.height);        // 创建画图区
        let offset,index,imgX,imgY,imgZ,gray,skeleton,tbss,rgb;
        let max = this.baseHeader.maxv;
        let min = this.baseHeader.minv;
        for(let x = 0;x < dims[1];x++){
          for(let y = 0;y < dims[2];y++){
            for(let z = 0;z < dims[3];z++){
              offset = ((((z * dims[2]) + y) * dims[1]) + x);
              imgX = (boxLen - dims[1]) / 2 + x;
              imgY = (boxLen - dims[2]) / 2 + y;
              imgZ = (boxLen - dims[3]) / 2 + z;
              index = ((((imgZ * boxLen) + imgY) * boxLen) + imgX)*4;
              gray = this.baseImage ? this.baseImage[offset] : 0;
              skeleton = this.skeletonImage ? this.skeletonImage[offset] : 0;
              tbss = this.tbssImage ? this.tbssImage[offset] : 0;
              if (gray !== 0) {
                if(max>1||min<0) imageData.data[index] = imageData.data[index + 1] = imageData.data[index + 2] = Math.floor((gray-min)/(max-min)*255);
                else imageData.data[index] = imageData.data[index + 1] = imageData.data[index + 2] = parseInt(gray * 255);
              }
              if (skeleton > 0.2) {
                imageData.data[index] = 0;
                imageData.data[index + 1] = 255;
                imageData.data[index + 2] = 0;
              }
              if (tbss !== 0) {
                rgb = Math.floor(tbss*100)*4
                imageData.data[index] = y2rColor[rgb];
                imageData.data[index + 1] = y2rColor[rgb + 1];
                imageData.data[index + 2] = y2rColor[rgb + 2];
              }
              if(gray !== 0)
                imageData.data[index + 3] = 255;//透明度
            }
          }
        }
        ctx.putImageData(imageData, 0, 0)
        // this.$el.appendChild(canvas);
        return build(canvas)
          .then(({colorCubeMesh, volMesh})=>{
            console.log({colorCubeMesh, volMesh})
            this.gl.colorCubeMat = colorCubeMesh.material;
            this.gl.volMat = volMesh.material;
            this.gl.rtTexture = volMesh.material.uniforms.tex.value;
            // volMesh.material.uniforms.tex.value = this.gl.rtTexture.texture;
            this.gl.scene1.add(colorCubeMesh);
            let edges = new THREE.EdgesGeometry(volMesh.geometry);
            let edgesMaterial = new THREE.LineBasicMaterial({
              color: 0xffffff
            })
            let line = new THREE.LineSegments(edges,edgesMaterial);
            this.gl.scene2.add(volMesh, line);
            this.gl.box = colorCubeMesh.geometry;
            return Promise.resolve()
          }).then(()=>{
            console.log('render')
            render()
            this.gl.controls.addEventListener('change', render)
          })
      },
      // 监听鼠标事件, 响应影像坐标变化
      handleMouseDown(event){
        this.mouseFlag = false;
        this.handleAxisChange(event);
      },
      handleAxisChange(event){
        if (this.mouseFlag) return;
        if (this.baseImage == null&&this.skeletonImage == null&&this.tbssImage == null) return;
        // 计算坐标axisX, axisY, axisZ
        // 画布大小为长420, 宽280, 分为三个视图区域
        // 区域一: 范围为x: 14~266, y: 0~280, 9:10
        // 区域二: 范围为x: 7~133, y: 287~413, 9:9
        // 区域三: 范围为x: 140~280, y: 287~413: 10:9 ( 高与区域二一致 )
        // 将位于区域的相对位置'线性变换'为核磁共振影像坐标axisX, axisY, axisZ, 并转为整型(用parseInt)
        let offsetX = event.offsetX;
        let offsetY = event.offsetY;
        let header = this.baseHeader;
        if (offsetY <= 280) {
          if (offsetX > 14 && offsetX <= 266)this.axisX = parseInt((offsetX-14)/252*header.dims[1]);
          else if (offsetX <= 14) this.axisX = 0;
          else if (offsetX > 266) this.axisX = header.dims[1] - 1;
          this.axisY = header.dims[2] - parseInt(offsetY/280*header.dims[2]);
          this.setScale({y:this.axisZ/header.dims[3]-0.5});
        }else if (offsetX < 140) {
          if (offsetX > 7 && offsetX <= 133)this.axisX = parseInt((offsetX-7)/126*header.dims[1]);
          else if (offsetX <= 7) this.axisX = 0
          else if (offsetX > 133) this.axisX = header.dims[1] - 1
          if (offsetY > 287 && offsetY <= 413)this.axisZ = header.dims[3] - parseInt((offsetY-287)/126*header.dims[3]);
          else if (offsetY <= 287) this.axisZ = header.dims[3] - 1
          else if (offsetY > 413) this.axisZ = 0
          this.setScale({z:this.axisY/header.dims[2]-0.5});
        }else if (offsetX <= 280){
          this.axisY = header.dims[2] - parseInt((offsetX-140)/140*header.dims[2]);
          if (offsetY > 287 && offsetY <= 413)this.axisZ = header.dims[3] - parseInt((offsetY-287)/126*header.dims[3]);
          else if (offsetY <= 287) this.axisZ = header.dims[3] - 1
          else if (offsetY > 413) this.axisZ = 0
          this.setScale({x:this.axisX/header.dims[1]-0.5});
        }
        // 绘图
        this.draw()
      },
      setScale({x=0.5,y=0.5,z=0.5,_x=-0.5,_y=-0.5,_z=-0.5}){
        //+++,++-,+-+,+--,-+-,-++,---,--+
        let box = this.gl.box;
        if(!box) return;
        let vertices = box.vertices
        vertices[0].set(x,y,z);
        vertices[1].set(x,y,_z);
        vertices[2].set(x,_y,z);
        vertices[3].set(x,_y,_z);
        vertices[4].set(_x,y,_z);
        vertices[5].set(_x,y,z);
        vertices[6].set(_x,_y,_z);
        vertices[7].set(_x,_y,z);
        box.verticesNeedUpdate = true;
        this.render()
      }
    }
  }
</script>

<style scoped>

</style>
